---
title: BackgroundTask
description: A library that provides an API for running background tasks.
sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-54/packages/expo-background-task'
packageName: 'expo-background-task'
platforms: ['android', 'ios', 'tvos']
hasVideoLink: true
---

import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';
import { Collapsible } from '~/ui/components/Collapsible';
import { Terminal } from '~/ui/components/Snippet';
import { PlatformTags } from '~/ui/components/Tag/PlatformTags';
import { VideoBoxLink } from '~/ui/components/VideoBoxLink';

`expo-background-task` provides an API to run deferrable background tasks in a way that optimizes battery and power consumption on the end user's device. This module uses the [`WorkManager`](https://developer.android.com/topic/libraries/architecture/workmanager) API on Android and the [`BGTaskScheduler`](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler) API on iOS to schedule tasks. It also uses the [`expo-task-manager`](task-manager.mdx) Native API to run JavaScript tasks.

<VideoBoxLink videoId="4lFus7TvayI" title="Watch: Expo Background Task Deep Dive" />

## Background tasks

A background task is a deferrable unit of work that is performed in the background, outside your app's lifecycle. This is useful for tasks that need to be executed when the app is inactive, such as syncing data with a server, fetching new content, or even checking if there are any [`expo-updates`](updates.mdx).

### When are background tasks run?

The Expo Background Task API leverages each platform to execute tasks at the most optimal time for both the user and the device when the app is in the background.

This means that the task may not run immediately after it is scheduled, but it will run at some point in the future if the system decides so. You can specify a minimum interval in minutes for the task to run. The task will execute sometime after the interval has passed, provided the specified conditions are met.

A background task will only run if the battery has enough charge (or the device is plugged into power) and the network is available. Without these conditions, the task won't execute. The exact behavior will vary depending on the operating system.

### When will they be stopped?

Background tasks are managed by platform APIs and system constraints. Knowing when tasks stop helps plan their use effectively.

- Background tasks are stopped if the user kills the app. Tasks resume when the app is restarted.
- If the system stops the app or the device reboots, background tasks will resume, and the app will be restarted.

On Android, removing an app from the recent apps list doesn't completely stop it, whereas on iOS, swiping it away in the app switcher fully terminates it.

> **Info** On Android, behavior varies by device vendor. For example, some implementations treat removing an app from the recent apps list as killing it. Read more about these differences here: [https://dontkillmyapp.com](https://dontkillmyapp.com).

## Platform differences

### Android&ensp;<PlatformTags platforms={['android']} />

On Android, the [`WorkManager`](https://developer.android.com/topic/libraries/architecture/workmanager) API allows specifying a minimum interval for a task to run (minimum 15 minutes). The task will execute sometime after the interval has passed, provided the specified conditions are met.

### iOS&ensp;<PlatformTags platforms={['ios']} />

On iOS, the [`BGTaskScheduler`](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler) API decides the best time to launch your background task. The system will consider the battery level, the network availability, and the user's usage patterns to determine when to run the task. You can still specify a minimum interval for the task to run, but the system may choose to run the task at a later time.

## Known limitations

### iOS&ensp;<PlatformTags platforms={['ios']} />

The [`Background Tasks`](https://developer.apple.com/documentation/backgroundtasks) API is unavailable on iOS simulators. It is only available when running on a physical device.

## Installation

<APIInstallSection />

## Configuration&ensp;<PlatformTags platforms={['ios']} />

To be able to run background tasks on iOS, you need to add the `processing` value to the `UIBackgroundModes` array in your app's **Info.plist** file. This is required for background fetch to work properly.

**If you're using [CNG](/workflow/continuous-native-generation/)**, the required `UIBackgroundModes` configuration will be applied automatically by prebuild.

<Collapsible summary="Configure UIBackgroundModes manually on iOS">

If you're not using Continuous Native Generation ([CNG](/workflow/continuous-native-generation/)), then you'll need to add the following to your **Info.plist** file:

```xml ios/project-name/Supporting/Info.plist
<key>UIBackgroundModes</key>
  <array>
    <string>processing</string>
  </array>
</key>
```

</Collapsible>

## Usage

Below is an example that demonstrates how to use `expo-background-task`.

```tsx App.tsx
import * as BackgroundTask from 'expo-background-task';
import * as TaskManager from 'expo-task-manager';
import { useEffect, useState } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';

const BACKGROUND_TASK_IDENTIFIER = 'background-task';

// Register and create the task so that it is available also when the background task screen
// (a React component defined later in this example) is not visible.
// Note: This needs to be called in the global scope, not in a React component.
TaskManager.defineTask(BACKGROUND_TASK_IDENTIFIER, async () => {
  try {
    const now = Date.now();
    console.log(`Got background task call at date: ${new Date(now).toISOString()}`);
  } catch (error) {
    console.error('Failed to execute the background task:', error);
    return BackgroundTask.BackgroundTaskResult.Failed;
  }
  return BackgroundTask.BackgroundTaskResult.Success;
});

// 2. Register the task at some point in your app by providing the same name
// Note: This does NOT need to be in the global scope and CAN be used in your React components!
async function registerBackgroundTaskAsync() {
  return BackgroundTask.registerTaskAsync(BACKGROUND_TASK_IDENTIFIER);
}

// 3. (Optional) Unregister tasks by specifying the task name
// This will cancel any future background task calls that match the given name
// Note: This does NOT need to be in the global scope and CAN be used in your React components!
async function unregisterBackgroundTaskAsync() {
  return BackgroundTask.unregisterTaskAsync(BACKGROUND_TASK_IDENTIFIER);
}

export default function BackgroundTaskScreen() {
  const [isRegistered, setIsRegistered] = useState<boolean>(false);
  const [status, setStatus] = useState<BackgroundTask.BackgroundTaskStatus | null>(null);

  useEffect(() => {
    updateAsync();
  }, []);

  const updateAsync = async () => {
    const status = await BackgroundTask.getStatusAsync();
    setStatus(status);
    const isRegistered = await TaskManager.isTaskRegisteredAsync(BACKGROUND_TASK_IDENTIFIER);
    setIsRegistered(isRegistered);
  };

  const toggle = async () => {
    if (!isRegistered) {
      await registerBackgroundTaskAsync();
    } else {
      await unregisterBackgroundTaskAsync();
    }
    await updateAsync();
  };

  return (
    <View style={styles.screen}>
      <View style={styles.textContainer}>
        <Text>
          Background Task Service Availability:{' '}
          <Text style={styles.boldText}>
            {status ? BackgroundTask.BackgroundTaskStatus[status] : null}
          </Text>
        </Text>
      </View>
      <Button
        disabled={status === BackgroundTask.BackgroundTaskStatus.Restricted}
        title={isRegistered ? 'Cancel Background Task' : 'Schedule Background Task'}
        onPress={toggle}
      />
      <Button title="Check Background Task Status" onPress={updateAsync} />
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  textContainer: {
    margin: 10,
  },
  boldText: {
    fontWeight: 'bold',
  },
});
```

## Multiple background tasks

Since the Background Tasks API on iOS and the WorkManager API on Android limit the number of tasks that can be scheduled for a single app, Expo Background Task uses a single worker on both platforms. While you can define multiple JavaScript background tasks, they will all run through this single worker.

The last registered background task determines the minimum interval for execution.

## Testing background tasks

Background tasks can be tested using the [`triggerTaskWorkerForTestingAsync`](#backgroundtasktriggertaskworkerfortestingasync) method. This method will run all registered tasks directly on Android and invoke the `BGTaskScheduler` on iOS. This is useful for testing the behavior of your background tasks without having to wait for the system to trigger them.

This method is only available in development mode. It will not work in production builds.

```tsx
import * as BackgroundTask from 'expo-background-task';
import { Button } from 'react-native';

function App() {
  const triggerTask = async () => {
    await BackgroundTask.triggerTaskWorkerForTestingAsync();
  };

  return <Button title="Trigger Background Task" onPress={triggerTask} />;
}
```

## Inspecting background tasks&ensp;<PlatformTags platforms={['android']} />

To troubleshoot or debug issues with background tasks on Android, use the `adb` tool included with the Android SDK to inspect scheduled tasks:

<Terminal
  cmd={['$ adb shell dumpsys jobscheduler | grep -A 40 -m 1 -E "JOB #.* <package-name>"']}
/>

The output from this command will show you the scheduled tasks for your app, including their status, constraints, and other information. Look for the `JOB` line to find the ID of the job and other details in the output:

```text
JOB #u0a453/275: 216a359 <package-name>/androidx.work.impl.background.systemjob.SystemJobService
  u0a453 tag=*job*/<package-name>/androidx.work.impl.background.systemjob.SystemJobService#275
  Source: uid=u0a453 user=0 pkg=<package-name>
  ...
  Required constraints: TIMING_DELAY CONNECTIVITY UID_NOT_RESTRICTED [0x90100000]
  Preferred constraints:
  Dynamic constraints:
  Satisfied constraints: CONNECTIVITY DEVICE_NOT_DOZING BACKGROUND_NOT_RESTRICTED TARE_WEALTH WITHIN_QUOTA UID_NOT_RESTRICTED [0x1b500000]
  Unsatisfied constraints: TIMING_DELAY [0x80000000]
  ...
  Enqueue time: -8m12s280ms
  Run time: earliest=+6m47s715ms, latest=none, original latest=none
  Restricted due to: none.
  Ready: false (job=false user=true !restricted=true !pending=true !active=true !backingup=true comp=true)
```

The first line contains the Job ID (275). The `Run time: earliest` value indicates the earliest time the task may start, while `enqueue time` shows how long ago the task was scheduled.

To force a task to run, use the `adb shell am broadcast` command. Move your app to the background before running this command, as the task will not run if the app is in the foreground.

<Terminal cmd={['$ adb shell cmd jobscheduler run -f <package-name> <JOB_ID>']} />

Where `JOB_ID` would be the identifier of the job you want to run that you found in the previous step.

## Troubleshooting background tasks&ensp;<PlatformTags platforms={['ios']} />

iOS does not have a tool similar to `adb` for inspecting background tasks. To test background tasks on iOS, use the built-in [`triggerTaskWorkerForTestingAsync`](#backgroundtasktriggertaskworkerfortestingasync) method. This method simulates the system triggering the task.

You can trigger this method from your app in debug mode (it does not work in production builds) to test the behavior of your background tasks without waiting for the system. If your background task configuration is incorrect, you will see the error description in the Xcode console:

```text
No task request with identifier com.expo.modules.backgroundtask.processing has been scheduled
```

The above error tells you that you need to run prebuild to apply the changes to your app's configuration.

This error also means you must run prebuild to apply your background task configuration to the app. Additionally, ensure you have defined and registered a background task as shown in [this example](#usage).

## API

```js
import * as BackgroundTask from 'expo-background-task';
```

<APISection packageName="expo-background-task" apiName="BackgroundTask" />
